home *** CD-ROM | disk | FTP | other *** search
/ The CICA Windows Explosion! / The CICA Windows Explosion! - Disc 2.iso / nt / nrlogind.zip / SESSION.C < prev    next >
C/C++ Source or Header  |  1994-02-18  |  14KB  |  535 lines

  1. /*
  2.  * Copyright (C) 1994 Nathaniel W. Mishkin.
  3.  * Copyright (C) 1991 Microsoft Corporation.
  4.  * All rights reserved.
  5.  */
  6.  
  7. #include <stdlib.h>
  8.  
  9. #include "session.h"
  10.  
  11. #define BUFFER_SIZE 200
  12.  
  13. //
  14. // Shell command line
  15. //
  16. #define SHELL_COMMAND_LINE  TEXT("cmd /q")
  17.  
  18. //
  19. // Structure used to describe each session
  20. //
  21. typedef struct {
  22.  
  23.     //
  24.     // These fields are filled in at session creation time
  25.     //
  26.     HANDLE  ReadPipeHandle;         // Handle to shell stdout pipe
  27.     HANDLE  WritePipeHandle;        // Handle to shell stdin pipe
  28.     HANDLE  ProcessHandle;          // Handle to shell process
  29.  
  30.     //
  31.     //
  32.     // These fields are filled in at session connect time and are only
  33.     // valid when the session is connected
  34.     //
  35.     SOCKET  ClientSocket;
  36.     HANDLE  ReadShellThreadHandle;  // Handle to session shell-read thread
  37.     HANDLE  WriteShellThreadHandle; // Handle to session shell-read thread
  38.  
  39. } SESSION_DATA, *PSESSION_DATA;
  40.  
  41.  
  42. //
  43. // Private prototypes
  44. //
  45.  
  46. static HANDLE
  47. StartShell(
  48.     HANDLE StdinPipeHandle,
  49.     HANDLE StdoutPipeHandle
  50.     );
  51.  
  52. static VOID
  53. SessionReadShellThreadFn(
  54.     LPVOID Parameter
  55.     );
  56.  
  57. static VOID
  58. SessionWriteShellThreadFn(
  59.     LPVOID Parameter
  60.     );
  61.  
  62.  
  63. // **********************************************************************
  64. // SessionLog
  65. //
  66. // Wrapper over WIN32 event logging functions.
  67. //
  68.  
  69. VOID
  70. SessionLog(
  71.     WORD EventType,
  72.     char *Fmt,
  73.     ...
  74. )
  75. {
  76.     HANDLE  hEventSource;
  77.     LPTSTR  lpszStrings[1];
  78.     char Msg[200];
  79.     va_list Marker;
  80.  
  81.     va_start(Marker, Fmt);
  82.     _vsnprintf(Msg, sizeof Msg, Fmt, Marker);
  83.  
  84.     puts(Msg);
  85.  
  86.     //
  87.     // Use event logging to log the error.
  88.     //
  89.     hEventSource = RegisterEventSource(NULL, TEXT("RloginService"));
  90.  
  91.     lpszStrings[0] = Msg;
  92.  
  93.     if (hEventSource != NULL) {
  94.         ReportEvent(hEventSource, EventType, 0, 0, NULL, 1, 0, lpszStrings, NULL);
  95.         DeregisterEventSource(hEventSource);
  96.     }
  97. }
  98.  
  99.  
  100. // **********************************************************************
  101. //
  102. // CreateSession
  103. //
  104. // Creates a new session. Involves creating the shell process and establishing
  105. // pipes for communication with it.
  106. //
  107. // Returns a handle to the session or NULL on failure.
  108. //
  109.  
  110. static PSESSION_DATA
  111. CreateSession(
  112.     VOID
  113.     )
  114. {
  115.     PSESSION_DATA Session = NULL;
  116.     BOOL Result;
  117.     SECURITY_ATTRIBUTES SecurityAttributes;
  118.     HANDLE ShellStdinPipe = NULL;
  119.     HANDLE ShellStdoutPipe = NULL;
  120.  
  121.     //
  122.     // Allocate space for the session data
  123.     //
  124.     Session = (PSESSION_DATA) malloc(sizeof(SESSION_DATA));
  125.     if (Session == NULL) {
  126.         return(NULL);
  127.     }
  128.  
  129.     //
  130.     // Reset fields in preparation for failure
  131.     //
  132.     Session->ReadPipeHandle  = NULL;
  133.     Session->WritePipeHandle = NULL;
  134.  
  135.  
  136.     //
  137.     // Create the I/O pipes for the shell
  138.     //
  139.     SecurityAttributes.nLength = sizeof(SecurityAttributes);
  140.     SecurityAttributes.lpSecurityDescriptor = NULL; // Use default ACL
  141.     SecurityAttributes.bInheritHandle = TRUE; // Shell will inherit handles
  142.  
  143.     Result = CreatePipe(&Session->ReadPipeHandle, &ShellStdoutPipe,
  144.                           &SecurityAttributes, 0);
  145.     if (!Result) {
  146.         SessionLog(EVENTLOG_ERROR_TYPE, "Failed to create shell stdout pipe, error = %d", 
  147.                    GetLastError());
  148.         goto Failure;
  149.     }
  150.  
  151.     Result = CreatePipe(&ShellStdinPipe, &Session->WritePipeHandle,
  152.                         &SecurityAttributes, 0);
  153.     if (!Result) {
  154.         SessionLog(EVENTLOG_ERROR_TYPE, "Failed to create shell stdin pipe, error = %d", 
  155.                    GetLastError());
  156.         goto Failure;
  157.     }
  158.  
  159.     //
  160.     // Start the shell
  161.     //
  162.     Session->ProcessHandle = StartShell(ShellStdinPipe, ShellStdoutPipe);
  163.  
  164.     //
  165.     // We're finished with our copy of the shell pipe handles
  166.     // Closing the runtime handles will close the pipe handles for us.
  167.     //
  168.     CloseHandle(ShellStdinPipe);
  169.     CloseHandle(ShellStdoutPipe);
  170.  
  171.     //
  172.     // Check result of shell start
  173.     //
  174.     if (Session->ProcessHandle == NULL) {
  175.         SessionLog(EVENTLOG_ERROR_TYPE, "Failed to execute shell");
  176.         goto Failure;
  177.     }
  178.  
  179.     //
  180.     // The session is not connected, initialize variables to indicate that
  181.     //
  182.     Session->ClientSocket = INVALID_SOCKET;
  183.  
  184.     //
  185.     // Success, return the session pointer as a handle
  186.     //
  187.     return(Session);
  188.  
  189. Failure:
  190.  
  191.     //
  192.     // We get here for any failure case.
  193.     // Free up any resources and exit
  194.     //
  195.  
  196.     if (ShellStdinPipe != NULL) 
  197.         CloseHandle(ShellStdinPipe);
  198.     if (ShellStdoutPipe != NULL) 
  199.         CloseHandle(ShellStdoutPipe);
  200.     if (Session->ReadPipeHandle != NULL) 
  201.         CloseHandle(Session->ReadPipeHandle);
  202.     if (Session->WritePipeHandle != NULL) 
  203.         CloseHandle(Session->WritePipeHandle);
  204.  
  205.     free(Session);
  206.  
  207.     return(NULL);
  208. }
  209.  
  210.  
  211. // **********************************************************************
  212. // SessionRun
  213. //
  214. // 
  215. // 
  216.  
  217. BOOL
  218. SessionRun(
  219.     SOCKET  ClientSocket
  220.     )
  221. {
  222.     PSESSION_DATA   Session = CreateSession();
  223.     SECURITY_ATTRIBUTES SecurityAttributes;
  224.     DWORD ThreadId;
  225.     HANDLE HandleArray[3];
  226.  
  227.     assert(ClientSocket != INVALID_SOCKET);
  228.  
  229.     SecurityAttributes.nLength = sizeof(SecurityAttributes);
  230.     SecurityAttributes.lpSecurityDescriptor = NULL; // Use default ACL
  231.     SecurityAttributes.bInheritHandle = FALSE; // No inheritance
  232.  
  233.     //
  234.     // Store the client socket handle in the session structure so the thread
  235.     // can get at it. This also signals that the session is connected.
  236.     //
  237.     Session->ClientSocket = ClientSocket;
  238.  
  239.     //
  240.     // Create the session threads
  241.     //
  242.     Session->ReadShellThreadHandle = 
  243.         CreateThread(&SecurityAttributes, 0,
  244.                      (LPTHREAD_START_ROUTINE) SessionReadShellThreadFn, 
  245.                      (LPVOID) Session, 0, &ThreadId);
  246.  
  247.     if (Session->ReadShellThreadHandle == NULL) {
  248.         SessionLog(EVENTLOG_ERROR_TYPE, 
  249.                    "Failed to create ReadShell session thread, error = %d", 
  250.                    GetLastError());
  251.  
  252.         //
  253.         // Reset the client pipe handle to indicate this session is disconnected
  254.         //
  255.         Session->ClientSocket = INVALID_SOCKET;
  256.         return(FALSE);
  257.     }
  258.  
  259.     Session->WriteShellThreadHandle = 
  260.         CreateThread(&SecurityAttributes, 0, 
  261.                      (LPTHREAD_START_ROUTINE) SessionWriteShellThreadFn, 
  262.                      (LPVOID) Session, 0, &ThreadId);
  263.  
  264.     if (Session->WriteShellThreadHandle == NULL) {
  265.         SessionLog(EVENTLOG_ERROR_TYPE, 
  266.                    "Failed to create ReadShell session thread, error = %d", 
  267.                     GetLastError());
  268.  
  269.         //
  270.         // Reset the client pipe handle to indicate this session is disconnected
  271.         //
  272.         Session->ClientSocket = INVALID_SOCKET;
  273.  
  274.         TerminateThread(Session->WriteShellThreadHandle, 0);
  275.         return(FALSE);
  276.     }
  277.  
  278.     //
  279.     // Wait for either thread or the shell process to finish
  280.     //
  281.  
  282.     HandleArray[0] = Session->ReadShellThreadHandle;
  283.     HandleArray[1] = Session->WriteShellThreadHandle;
  284.     HandleArray[2] = Session->ProcessHandle;
  285.  
  286.     switch (WaitForMultipleObjects(3, HandleArray, FALSE, 0xffffffff)) {
  287.       case WAIT_OBJECT_0 + 0:
  288.         TerminateThread(Session->WriteShellThreadHandle, 0);
  289.         TerminateProcess(Session->ProcessHandle, 1);
  290.         break;
  291.  
  292.       case WAIT_OBJECT_0 + 1:
  293.         TerminateThread(Session->ReadShellThreadHandle, 0);
  294.         TerminateProcess(Session->ProcessHandle, 1);
  295.         break;
  296.  
  297.       case WAIT_OBJECT_0 + 2:
  298.         TerminateThread(Session->WriteShellThreadHandle, 0);
  299.         TerminateThread(Session->ReadShellThreadHandle, 0);
  300.         break;
  301.  
  302.       default:
  303.         SessionLog(EVENTLOG_ERROR_TYPE, "WaitForMultipleObjects error: %d", 
  304.                    GetLastError());
  305.         break;
  306.     }
  307.  
  308.     //
  309.     // Close my handles to the threads, the shell process, and the shell pipes
  310.  
  311.     CloseHandle(Session->ReadShellThreadHandle);
  312.     CloseHandle(Session->WriteShellThreadHandle);
  313.     CloseHandle(Session->ProcessHandle);
  314.     CloseHandle(Session->ReadPipeHandle);
  315.     CloseHandle(Session->WritePipeHandle);
  316.  
  317.     closesocket(Session->ClientSocket);
  318.  
  319.     free(Session);
  320.  
  321.     return(TRUE);
  322. }
  323.  
  324.  
  325. // **********************************************************************
  326. //
  327. // StartShell
  328. //
  329. // Execs the shell with the specified handle as stdin, stdout/err
  330. //
  331. // Returns process handle or NULL on failure
  332. //
  333.  
  334. static HANDLE
  335. StartShell(
  336.     HANDLE ShellStdinPipeHandle,
  337.     HANDLE ShellStdoutPipeHandle
  338.     )
  339. {
  340.     PROCESS_INFORMATION ProcessInformation;
  341.     STARTUPINFO si;
  342.     HANDLE ProcessHandle = NULL;
  343.  
  344.     //
  345.     // Initialize process startup info
  346.     //
  347.     si.cb = sizeof(STARTUPINFO);
  348.     si.lpReserved = NULL;
  349.     si.lpTitle = NULL;
  350.     si.lpDesktop = NULL;
  351.     si.dwX = si.dwY = si.dwXSize = si.dwYSize = 0L;
  352.     si.wShowWindow = SW_SHOW;
  353.     si.lpReserved2 = NULL;
  354.     si.cbReserved2 = 0;
  355.  
  356.     si.dwFlags = STARTF_USESTDHANDLES;
  357.  
  358.     si.hStdInput  = ShellStdinPipeHandle;
  359.     si.hStdOutput = ShellStdoutPipeHandle;
  360.  
  361.     DuplicateHandle(GetCurrentProcess(), ShellStdoutPipeHandle, 
  362.                     GetCurrentProcess(), &si.hStdError,
  363.                     DUPLICATE_SAME_ACCESS, TRUE, 0);
  364.  
  365.     if (CreateProcess(NULL, SHELL_COMMAND_LINE, NULL, NULL, TRUE, 0, NULL, NULL,
  366.                       &si, &ProcessInformation)) 
  367.     {
  368.         ProcessHandle = ProcessInformation.hProcess;
  369.         CloseHandle(ProcessInformation.hThread);
  370.     } 
  371.     else 
  372.         SessionLog(EVENTLOG_ERROR_TYPE, "Failed to execute shell, error = %d", 
  373.                    GetLastError());
  374.  
  375.     return(ProcessHandle);
  376. }
  377.  
  378.  
  379. // **********************************************************************
  380. // SessionReadShellThreadFn
  381. //
  382. // The read thread procedure. Reads from the pipe connected to the shell
  383. // process, writes to the socket.
  384. //
  385.  
  386. static VOID
  387. SessionReadShellThreadFn(
  388.     LPVOID Parameter
  389.     )
  390. {
  391.     PSESSION_DATA Session = Parameter;
  392.     BYTE    Buffer[BUFFER_SIZE];
  393.     BYTE    Buffer2[BUFFER_SIZE+30];
  394.     DWORD   BytesRead;
  395.  
  396.     while (ReadFile(Session->ReadPipeHandle, Buffer, sizeof(Buffer), 
  397.                     &BytesRead, NULL)) 
  398.     {
  399.         DWORD BufferCnt, BytesToWrite;
  400.         BYTE PrevChar = 0;
  401.  
  402.         //
  403.         // Process the data we got from the shell:  replace any naked LF's
  404.         // with CR-LF pairs.
  405.         //
  406.         for (BufferCnt = 0, BytesToWrite = 0; BufferCnt < BytesRead; BufferCnt++) {
  407.             if (Buffer[BufferCnt] == '\n' && PrevChar != '\r')
  408.                 Buffer2[BytesToWrite++] = '\r';
  409.             PrevChar = Buffer2[BytesToWrite++] = Buffer[BufferCnt];
  410.             assert(BytesToWrite < sizeof Buffer2);
  411.         }
  412.  
  413.         if (send(Session->ClientSocket, Buffer2, BytesToWrite, 0) <= 0) 
  414.             break;
  415.     }
  416.  
  417.     if (GetLastError() != ERROR_BROKEN_PIPE)
  418.         SessionLog(EVENTLOG_ERROR_TYPE, "SessionReadShellThreadFn exitted, error = %ld", 
  419.                    GetLastError());
  420. }
  421.  
  422.  
  423. // **********************************************************************
  424. // SessionWriteShellThreadFn
  425. //
  426. // The write thread procedure. Reads from socket, writes to pipe connected
  427. // to shell process.  We process certain special character that arrive on
  428. // the socket: backspace (BS), delete (DEL), and ^U are take to be
  429. // line editing controls; ^C is interrupt.
  430. //
  431.  
  432. #define CHAR_BS        0010
  433. #define CHAR_DEL    0177
  434. #define CHAR_CTRL_C    0003
  435. #define CHAR_CTRL_U    0025
  436.  
  437. static VOID
  438. SessionWriteShellThreadFn(
  439.     LPVOID Parameter
  440.     )
  441. {
  442.     PSESSION_DATA Session = Parameter;
  443.     BYTE    RecvBuffer[1];
  444.     BYTE    Buffer[BUFFER_SIZE];
  445.     BYTE    EchoBuffer[5];
  446.     DWORD   BytesWritten;
  447.     DWORD   BufferCnt, EchoCnt;
  448.     DWORD   TossCnt = 0;
  449.     BOOL    PrevWasFF = FALSE;
  450.  
  451.     BufferCnt = 0;
  452.  
  453.     //
  454.     // Loop, reading one byte at a time from the socket.    
  455.     //
  456.     while (recv(Session->ClientSocket, RecvBuffer, sizeof(RecvBuffer), 0) > 0) {
  457.         //
  458.         // If tossing, then toss.  This is to ignore window size control
  459.         // messages.
  460.         //
  461.         if (TossCnt > 0) {
  462.             TossCnt -= 1;
  463.             continue;
  464.         }
  465.  
  466.         //
  467.         // Check for window size control message (12 bytes: FF FF ...).
  468.         // We just want to ignore it.  See RFC 1282. 
  469.         //
  470.         if (RecvBuffer[0] != 0xff)
  471.             PrevWasFF = FALSE;
  472.         else {
  473.             if (! PrevWasFF) 
  474.                 PrevWasFF = TRUE;
  475.             else {
  476.                 TossCnt = 10;
  477.                 PrevWasFF = FALSE;
  478.             }
  479.             continue;
  480.         }
  481.  
  482.         EchoCnt = 0;
  483.  
  484.         //
  485.         // See if the byte is special
  486.         //
  487.  
  488.         if (RecvBuffer[0] == CHAR_BS || RecvBuffer[0] == CHAR_DEL) {
  489.             if (BufferCnt > 0) {
  490.                 BufferCnt -= 1;
  491.                 EchoBuffer[EchoCnt++] = CHAR_BS;
  492.                 EchoBuffer[EchoCnt++] = ' ';
  493.                 EchoBuffer[EchoCnt++] = CHAR_BS;
  494.             }
  495.         }
  496.         else if (RecvBuffer[0] == CHAR_CTRL_C) {
  497.             GenerateConsoleCtrlEvent(CTRL_C_EVENT, 0);
  498.         }
  499.         else if (RecvBuffer[0] == CHAR_CTRL_U) {
  500.             BufferCnt = 0;
  501.             EchoBuffer[EchoCnt++] = ' ';
  502.             EchoBuffer[EchoCnt++] = 'X';
  503.             EchoBuffer[EchoCnt++] = 'X';
  504.             EchoBuffer[EchoCnt++] = 'X';
  505.             EchoBuffer[EchoCnt++] = '\r';
  506.             EchoBuffer[EchoCnt++] = '\n';
  507.         }
  508.         else {
  509.             Buffer[BufferCnt++] = EchoBuffer[EchoCnt++] = RecvBuffer[0];
  510.             if (RecvBuffer[0] == '\r')
  511.                 Buffer[BufferCnt++] = EchoBuffer[EchoCnt++] = '\n';
  512.         }
  513.  
  514.         //
  515.         // If there's anything to echo back over the socket, do it now.
  516.         //
  517.         if (EchoCnt > 0 && send(Session->ClientSocket, EchoBuffer, EchoCnt, 0) <= 0) 
  518.             break;
  519.         
  520.         //
  521.         // If we got a CR, it's time to send what we've buffered up down to the
  522.         // shell process.
  523.         //
  524.         if (RecvBuffer[0] == '\r') {
  525.             if (! WriteFile(Session->WritePipeHandle, Buffer, BufferCnt, 
  526.                             &BytesWritten, NULL))
  527.             {
  528.                 break;
  529.             }
  530.             BufferCnt = 0;
  531.         }
  532.     }
  533. }
  534.  
  535.